package com.birdboot.core;

import com.birdboot.annotations.Controller;
import com.birdboot.annotations.RequestMapping;
import com.birdboot.controller.UserController;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;

/*
* V8新增内容;
* 该类是SpringMVC框架与Tomcat整合时的一个关键类
* Tomcat处理业务原生的都是调用继承了HttpServlet的类来完成，此时需要进行很多配置
* 以及使用时要做很多重复性劳动
* SpringMVC框架提供的该类也继承了HttpServlet的，使用它来接收处理请求的工作
* */
public class DispatcherServlet {
    private static File baseDir; //类加载路径
    private static File staticDir;//类加载路径中的static目录
    static {
        //定位当前项目的类加载路径
        try {
            baseDir = new File(
                    DispatcherServlet.class.getClassLoader().getResource(".").toURI()
            );
            //定位类加载路径下的static目录
            staticDir = new File(baseDir,"static");
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }
    /*
    * service方法实际上是当我们继承了HttpServlet后必须重写的方法
    * 该方法要求接收两个参数：请求对象与响应对象
    * Tomcat在处理请求时就是调用某个Servlet的service方法并将请求与响应对象传入来让其完成处理工作的
    * */
    public void service(HttpServletRequest request, HttpServletResponse response) {
        //获取请求的抽象路径
        //不能再使用uri判断请求了，因为uri可能含有参数，内容不固定
        String path = request.getRequestURI();
        System.out.println(path);
        /*
        * v17新增内容
        * 当我们得到本次请求路径path的值后，我们首先要查看是否为请求业务
        * 1：扫描controller包下的所有类
        * 2：查看哪些被注解@Controller标注过的类(只有被该注解标注过的类才认可为业务处理类)
        * 3：便利这些类，并获取他们的所有方法，并查看哪些是业务方法
        * 只有被注解@RequestMapping标注的方法才是业务方法
        * 4：遍历业务方法时比对该方法上@RequestMapping中传递的参数值是否与本次请求路径path值一致？如果一致则说明本次请求就应当由该方法进行处理，因此利用反射机调用该方法进行处理。
        * 提示：反射机制调用后要记得return，避免执行后续处理静态资源
        * 5：如果扫描了所有的Controller中所有方法，均未找到与本次请求匹配的路径
        * 则说明本次请求并非处理业务，那么执行下面请求静态资源的操作
        * */
        //定位controller目录 使用File访问文件系统时，路径中目录的层级分割是用"/"
        try {
            File dir = new File(baseDir,"com/birdboot/controller");
            //获取该目录下的所有.class文件
            File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
            for (File sub:subs){ //遍历每一个.class文件
                String fileName = sub.getName(); //获取文件名
                String className = fileName.substring(0,sub.getName().indexOf(".")); //根据文件名获取类名
                //包名的层级分割使用"."
                Class cls = Class.forName("com.birdboot.controller."+className);
                //判断该类有没有注解Controller
                if(cls.isAnnotationPresent(Controller.class)){
                    //扫描该类的所有方法
                    Method[] methods = cls.getDeclaredMethods();
                    //遍历每一个方法
                    for (Method method:methods){
                        //判断该方法是否有注解RequestMapping
                        if(method.isAnnotationPresent(RequestMapping.class)){
                            //获取注解
                            RequestMapping rm = method.getAnnotation(RequestMapping.class);
                            //通过注解对象获取参数
                            String value = rm.value();
                            //对比本次请求是否与该方法注解参数一致
                            if(path.equals(value)){
                                //一致则说明本次请求应当交由该方法处理
                                //实例化该Controller
                                Object obj = cls.newInstance();
                                method.invoke(obj,request,response);
                                return;
                            }
                            System.out.println(rm.value());
                        }
                    }
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        File file = new File(staticDir, path);
        if (file.isFile()) {
            //由于响应对象中状态代码和描述默认值是200，OK因此正确情况下不用再设置
            response.setContentFile(file);
            response.addHeader("Server", "BirdServer");
        } else {
            response.setStatusCode(404);
            response.setStatusReason("NotFound");
            file = new File(staticDir, "404.html");
            response.setContentFile(file);

            response.addHeader("Server", "BirdServer");
        }
    }
}
